﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Av.Utils;

namespace JrSnifTest
{

    /// <summary>
    /// Interface to be used for IP packet filtering
    /// </summary>
    public interface IPacketMatcher
    {
        bool Match(IpHeader header);
        bool IpProtocolMatch(byte[] ba);

        void AddIpProtocol(int protocol);
        void AddIp(string ip);
        void AddPort(int port);
    }

    class PacketMatcher : IPacketMatcher
    {
        /// <summary>
        /// Ports used to filter
        /// </summary>
        protected int[] m_arPorts = new int[65535];

        /// <summary>
        /// IPs to filter
        /// </summary>
        protected List<string> m_arIps = new List<string>();

        /// <summary>
        /// IP protocols to filter
        /// </summary>
        protected int[] m_arIpProtocols = new int[256];

        /// <summary>
        /// Indicates whether port or IP protocol were added
        /// </summary>
        protected bool m_isPortOrProtocol = false;

        /// <summary>
        /// Gets true when filter is empty, not initialized
        /// </summary>
        public bool IsEmpty { get { return (m_isPortOrProtocol == false) && (m_arIps.Count < 1); } }

        #region IPacketMatcher Members

        public bool Match(IpHeader header)
        {
            bool isMatch = false;

            try
            {
                // Allow everything if filter is not initialized
                if( IsEmpty )
                {
                    return true;
                }

                // First check remote IP
                if(m_arIps.Contains(header.DestinationIp))
                {
                    // Check remote port only for TCP
                    if(header.TcpHeader != null)
                    {
                        isMatch = m_arPorts[header.TcpHeader.DestinationPort] != 0;
                    } else
                    {
                        // Ok event w/o port :)
                        isMatch = true;
                    }
                }
            } catch(Exception ex)
            {
                Log4cs.Log(Importance.Error, "Error matching IP packet!");
                Log4cs.Log(Importance.Debug, ex.ToString());
                isMatch = false;
            }

            return isMatch;
        }

        public bool IpProtocolMatch(byte[] ba)
        {
            bool isMatch = false;

            try
            {

                // Allow everything if filter is not initialized
                if(IsEmpty)
                {
                    return true;
                }

                int protocol = IpHeader.GetProtocol(ba, 0);
                isMatch = m_arIpProtocols[protocol] != 0;
            } catch(Exception ex)
            {
                Log4cs.Log(Importance.Error, "Error matching IP protocol!");
                Log4cs.Log(Importance.Debug, ex.ToString());
                isMatch = false;
            }

            return isMatch;
        }

        public void AddIpProtocol(int protocol)
        {
            m_arIpProtocols[protocol] = protocol;
        }

        public void AddIp(string ip)
        {
            m_arIps.Add(ip);
        }

        public void AddPort(int port)
        {
            m_arPorts[port] = port;
        }

        #endregion

        public override string ToString()
        {
            StringBuilder sb = new StringBuilder();

            for(int i = 0; i < m_arIpProtocols.Length; i++)
            {
                if(m_arIpProtocols[i] != 0)
                {
                    sb.AppendLine("0x" + m_arIpProtocols[i].ToString("X02"));
                }
            }

            if(m_arIps.Count > 0)
            {
                sb.AppendLine("IP to filter by:");
                for(int i = 0; i < m_arIps.Count; i++)
                {
                    sb.AppendLine("  " + m_arIps[i]);
                }
            } else
            {
                sb.AppendLine("Do not filter by IP");
            }

            sb.AppendLine("Ports to filter by:");
            for(int i = 0; i < m_arPorts.Length; i++)
            {
                if( m_arPorts[i] != 0 )
                {
                    sb.AppendLine("  " + m_arPorts[i]);
                }
            }

            return sb.ToString();
        }
    }
}
